#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string>

using namespace std;

int main()
{
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0)
    {
        perror("socket:");
        return 0;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(45678);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("bind:");
        return 0;
    }

    ret = listen(listen_sockfd, 5);
    if(ret < 0)
    {
        perror("listen:");
        return 0;
    }

    int epfd = epoll_create(6);
    if(epfd < 0)
    {
        perror("epoll_create:");
        return 0;
    }

    struct epoll_event ee;
    ee.events = EPOLLIN;
    ee.data.fd = listen_sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sockfd, &ee);

    while(1)
    {
        struct epoll_event arr[10];
        int ret = epoll_wait(epfd, arr, 10, -1);
        if(ret < 0)
        {
            perror("epoll_wait:");
            return 0;
        }
        else if(ret == 0)
        {
            continue;
        }

        //对就绪的事件进行遍历，看是哪一个，分别处理
        for(int i = 0; i < ret; i++)
        {
            //liste_sockfd采用LT模式
            if(arr[i].data.fd == listen_sockfd)
            {
                int newsockfd = accept(listen_sockfd, NULL, NULL);//accept的参数对面的地址信息结构和长度
                if(newsockfd < 0) //accept的返回值小于0失败，大于等于0成功
                {
                    perror("accept:");
                    continue; //失败 继续接收
                }

                //下面表示接收到的新连接，让新连接采用ET模式
                int flag = fcntl(newsockfd, F_GETFL);
                fcntl(newsockfd, F_SETFL, flag | O_NONBLOCK);


                struct epoll_event new_ee;
                new_ee.events = EPOLLIN | EPOLLET; //将事件改为ET模式
                new_ee.data.fd = newsockfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, newsockfd, &new_ee);
            }
            else 
            {
                int newsockfd = arr[i].data.fd;
                
                string recv_buf;

                int flag = true;

                while(1) //因为ET模式只通知一次，循环接收
                {
                    char buf[2] = {0}; //buf预留了\n的位置
                    ssize_t recv_size = recv(newsockfd, buf, sizeof(buf) - 1, 0); //0表示阻塞接收
                    if(recv_size < 0)
                    {
                        if(errno == EAGAIN || errno == EWOULDBLOCK)
                        {
                            flag = true;
                            break;
                        }
                        perror("recv:");
                        flag = false;
                        break;
                    }
                    else if(recv_size == 0) //对端关闭
                    {
                        printf("perr shut down connect\n");
                        epoll_ctl(epfd, EPOLL_CTL_DEL, newsockfd, NULL); 
                        close(newsockfd);
                        flag = false;
                        break;
                    }
                    recv_buf.append(buf);
                }
                if(flag == true)
                {
                    printf("recv_buf:%s\n", recv_buf.c_str());
                    char buf[1024] = {0};
                    memset(buf, '\0', sizeof(buf));
                    strncpy(buf, "hello, i am server~~~", sizeof(buf));
                    send(newsockfd, buf, strlen(buf), 0);
                }
            }
        }
    }

    epoll_ctl(epfd, EPOLL_CTL_DEL, listen_sockfd, NULL);
    close(listen_sockfd);
    return 0;
}
